package com.desaysv.someip;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import androidx.annotation.NonNull;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author uidq5208
 * @version 1.0
 * @since 2023-3-7
 */
public final class SomeIpManager {
    //*********************自定义 start*************************//
    /**
     * PIVI服务id
     */
    private static final int PIVI_SERVICE_ID = 0x01;

    /**
     * APB id
     */
    private static final int APB_CLIENT_ID = 0x02;

    /**
     * udp连接对象
     */
    private static final int READER_PORT = 8081;
    /**
     * PIVI服务的端口
     */
    private static final int SENDER_PORT = 8082;
    //**********************自定义 end ************************//


    /**
     * 构造函数
     */
    private SomeIpManager() {
        init();
    }

    /**
     * 单例对象
     */
    private static volatile SomeIpManager mInstance = null;

    /**
     * 获取单例对象
     *
     * @return SomeIpManager
     */
    public static SomeIpManager getInstance() {
        if (mInstance == null) {
            synchronized (SomeIpManager.class) {
                if (mInstance == null) {
                    mInstance = new SomeIpManager();
                }
            }
        }
        return mInstance;
    }

    /**
     * 最大线程数
     */
    private static final int THREAD_MAX_NUM = 10;
    /**
     * 但个包最大长度
     */
    private static final int UDP_PACKET_LENGTH = 1024;

    /**
     * 重试最大次数
     */
    private static final int MAX_RETRY = 3;

    /**
     * 响应超时时间 （ms）
     */
    private static final int TIME_OUT = 50;

    /**
     * 重试缓存池
     */
    private final ConcurrentHashMap<Integer, Integer> mRetryCountPool = new ConcurrentHashMap<>();

    /**
     * 日志TAG
     */
    private static final String TAG = "[SomeIpManager]";

    /**
     * 主线程Handler对象
     */
    private final Handler mUIHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public synchronized void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            Integer retryCount = mRetryCountPool.get(msg.what);
            if (retryCount == null) {
                return;
            }
            if (retryCount >= MAX_RETRY) {
                SomeIpMessager messager = SomeIpMessager.decoder((byte[]) msg.obj, false);
                getObservable().post(OBSERVER_TAG_TIMEOUT, messager);
                mRetryCountPool.remove(msg.what);
            } else {
                retryCount++;
                Log.i(TAG, "handleMessage: retry===>" + msg.what);
                mRetryCountPool.put(msg.what, retryCount);
                SomeIpMessager.MsgType msgType = SomeIpMessager.cover(msg.arg1);
                sendToServer(msg.what, (byte[]) msg.obj, msgType);
            }
        }
    };
    /**
     * 线程池管理
     */
    private final ExecutorService mThreadPool = Executors.newFixedThreadPool(THREAD_MAX_NUM);

    /**
     * 连接对象
     */
    private DatagramSocket mReceiverSocket;

    /**
     * 服务是否创建
     */
    private boolean mReaderStart = false;


    /**
     * 初始化操作
     */
    private void init() {
        if (mReceiverSocket != null) {
            return;
        }
        mReaderStart = true;
        try {
            mReceiverSocket = new DatagramSocket(READER_PORT);
            Log.i(TAG, "init: " + mReceiverSocket.isClosed());
            mThreadPool.execute(() -> {
                while (mReaderStart) {
                    try {
                        byte[] info = new byte[UDP_PACKET_LENGTH];
                        DatagramPacket packet = new DatagramPacket(info, info.length);
                        mReceiverSocket.receive(packet);
                        Log.i(TAG, "receiver=>{address:" + packet.getAddress() + ",port:" + packet.getPort() + "}");
                        //处理消息
                        dealResponse(packet.getData());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    /**
     * 释放操作
     */
    public synchronized void destroy() {
        if (mReaderStart) {
            mReaderStart = false;
        }
        if (mReceiverSocket != null && !mReceiverSocket.isClosed()) {
            mReceiverSocket.close();
        }
        mReceiverSocket = null;

        if (!mThreadPool.isShutdown()) {
            mThreadPool.shutdown();
        }

        mSomeIpObservable.deleteObservers();
        mUIHandler.removeCallbacksAndMessages(null);
        mRetryCountPool.clear();
        mInstance = null;
    }

    /**
     * 处理响应
     *
     * @param data 响应数据
     */
    private void dealResponse(byte[] data) {
        SomeIpMessager messager = SomeIpMessager.decoder(data, false);
        if (messager == null) {
            Log.e(TAG, "dealResponse: error");
            return;
        }
        int sessionId = messager.getSessionId();
        if (mRetryCountPool.containsKey(sessionId)) {
            mRetryCountPool.remove(sessionId);
            mUIHandler.removeMessages(sessionId);
        }
        getObservable().post(OBSERVER_TAG_REP, messager);
    }

    /**
     * 发送消息
     *
     * @param methodId 方法id
     * @param payload  payload值
     */
    public void send(int methodId, byte[] payload) {
        send(methodId, payload, SomeIpMessager.MsgType.REQUEST);
    }

    public void send(int methodId, byte[] payload, SomeIpMessager.MsgType msgType) {
        if (!mReaderStart) {
            return;
        }
        SomeIpMessager.Buidler buidler = new SomeIpMessager.Buidler(PIVI_SERVICE_ID);
        buidler.setMethodId(methodId);
        buidler.setClientId(APB_CLIENT_ID);
        buidler.setMsgType(msgType);
        byte[] data;
        if (payload != null) {
            data = buidler.builder(payload, false);
        } else {
            data = buidler.builder(false);
        }
        Log.i(TAG, "send: " + SomeIpMessager.byteArrayToHexString(data));
        int sessionId = buidler.getSessionId();
        Log.i(TAG, "sessionId: " + sessionId);
        sendToServer(sessionId, data, buidler.getMsgType());
    }

    /**
     * 实际消息发送【异步】
     *
     * @param sessionId 会话id
     * @param data      实际发送的数据
     * @param msgType 消息类型，部分类型消息不需要超时
     */
    private void sendToServer(int sessionId, byte[] data, SomeIpMessager.MsgType msgType) {
        if (mReceiverSocket == null || mReceiverSocket.isClosed()) {
            return;
        }
        mThreadPool.execute(() -> {
            try {
//                DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("127.0.0.1"), SENDER_PORT);
                DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("10.217.8.59"), SENDER_PORT);
                mReceiverSocket.send(packet);
                Log.i(TAG, "sendToServer=>" + packet.getAddress() + ":" + packet.getPort());
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (msgType == SomeIpMessager.MsgType.REQUEST || msgType == SomeIpMessager.MsgType.REQUEST_ACK) {
                Message message = Message.obtain();
                message.what = sessionId;
                message.obj = data;
                message.arg1 = msgType.getValue();
                mRetryCountPool.putIfAbsent(sessionId, 0);
                mUIHandler.sendMessageDelayed(message, TIME_OUT);
            }
        });

    }


    /**
     * 响应TAG
     */
    private static final int OBSERVER_TAG_REP = 0;
    /**
     * 超时TAG
     */
    private static final int OBSERVER_TAG_TIMEOUT = 1;
    /**
     * SomeIpObservable对象
     */
    private volatile SomeIpObservable mSomeIpObservable = null;

    /**
     * 获取观察者对象
     *
     * @return SomeIpObservable
     */
    private SomeIpObservable getObservable() {
        if (mSomeIpObservable == null) {
            synchronized (SomeIpObservable.class) {
                if (mSomeIpObservable == null) {
                    mSomeIpObservable = new SomeIpObservable();
                }
            }
        }
        return mSomeIpObservable;
    }

    /**
     * 添加观察者回调
     *
     * @param someIpObserver 回调对象
     */
    public void addObserver(SomeIpObserverImpl someIpObserver) {
        getObservable().addObserver(someIpObserver);
    }

    /**
     * 移除观察者回调
     *
     * @param someIpObserver 回调对象
     */
    public void removeObserver(SomeIpObserverImpl someIpObserver) {
        getObservable().deleteObserver(someIpObserver);
    }

    /**
     * 观察者事件类
     */
    private static class Event {
        /**
         * tag值，区分业务
         */
        private int tag = OBSERVER_TAG_REP;
        /**
         * 消息对象
         */
        private SomeIpMessager someIpMessager = null;

        /**
         * 构造方法
         *
         * @param tag      tag值
         * @param messager 消息对象
         */
        Event(int tag, SomeIpMessager messager) {
            this.tag = tag;
            this.someIpMessager = messager;
        }
    }

    /**
     * SomeIpObservable 对象
     */
    private static class SomeIpObservable extends Observable {

        /**
         * 消息分发
         *
         * @param tag  事件TAG
         * @param data 事件data
         */
        public void post(int tag, SomeIpMessager data) {
            setChanged();
            notifyObservers(new Event(tag, data));
        }
    }

    /**
     * 观察者回调接口
     */
    private interface ISomeIpObserver extends Observer {
        /**
         * 响应接口
         *
         * @param messager 响应内容，payload内容需要业务自行解析
         */
        void response(SomeIpMessager messager);

        /**
         * 超时接口
         *
         * @param methodId  事件id
         * @param sessionId 会话id
         */
        void timeout(int methodId, int sessionId);
    }

    /**
     * 观察者回调分发对象
     */
    public abstract static class SomeIpObserverImpl implements ISomeIpObserver {
        @Override
        public void update(Observable o, Object arg) {
            if (!(arg instanceof Event)) {
                return;
            }
            Event event = (Event) arg;
            SomeIpManager.getInstance().mUIHandler.post(() -> {
                if (event.tag == OBSERVER_TAG_REP) {
                    response(event.someIpMessager);
                } else if (event.tag == OBSERVER_TAG_TIMEOUT) {
                    timeout(event.someIpMessager.getMethodId(), event.someIpMessager.getSessionId());
                }
            });
        }
    }
}
